﻿// ConsoleApplication3.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <windows.h>
#include <math.h>
#include <time.h>

// 窗口与粒子配置
#define WINDOW_W 800    // 窗口宽度
#define WINDOW_H 600    // 窗口高度
#define MAX_PART 1500   // 最大粒子数（更多粒子更显实心）
#define HEART_SCALE 12  // 爱心缩放比例（越大爱心越大）

// 粒子结构体
typedef struct {
    double x;      // X坐标
    double y;      // Y坐标
    double vx;     // X方向速度
    double vy;     // Y方向速度
    int r;         // 粒子半径
    COLORREF col;  // 粒子颜色
    int life;      // 生命周期（帧）
} Particle;

Particle particles[MAX_PART];  // 粒子数组
int partCount = 0;             // 当前粒子数量
HDC hdc;                       // GDI 设备上下文

// 生成爱心区域内的随机点（确保粒子填充爱心内部，实现实心效果）
void getRandomHeartPoint(double* x, double* y) {
    double t = (double)rand() / RAND_MAX * 360;  // 0-360度随机角度
    double angle = t * 3.1415926 / 180;
    double scaleRand = 0.3 + (double)rand() / RAND_MAX * 0.7;  // 0.3-1.0随机缩放（填充内部）

    // 爱心极坐标公式（生成轮廓+内部点）
    *x = 16 * pow(sin(angle), 3) * scaleRand;
    *y = (13 * cos(angle) - 5 * cos(2 * angle) - 2 * cos(3 * angle) - cos(4 * angle)) * scaleRand;

    // 缩放并平移到窗口中心（Y轴反转，因GDI Y轴向下）
    *x = *x * HEART_SCALE + WINDOW_W / 2;
    *y = -(*y) * HEART_SCALE + WINDOW_H / 2;
}

// 初始化单个粒子（从爱心区域内生成）
void initParticle() {
    if (partCount >= MAX_PART) return;
    Particle* p = &particles[partCount++];

    // 从爱心区域取初始位置
    getRandomHeartPoint(&p->x, &p->y);

    // 随机速度（轻微扩散，模拟粒子流动）
    p->vx = (rand() % 10 - 5) / 8.0;   // X速度：-0.625 ~ 0.625
    p->vy = (rand() % 10 - 5) / 8.0;   // Y速度：-0.625 ~ 0.625

    // 粒子属性（半径2-4像素，粉色系渐变）
    p->r = rand() % 3 + 2;
    p->col = RGB(255, rand() % 80 + 175, rand() % 120 + 135);  // 浅粉到深粉
    p->life = rand() % 80 + 60;  // 生命周期60-140帧（更持久，填充更密）
}

// GDI绘制实心圆（粒子）
void drawSolidCircle(int x, int y, int r) {
    HBRUSH brush = CreateSolidBrush(hdc->CurrentBrushColor);
    SelectObject(hdc, brush);
    Ellipse(hdc, x - r, y - r, x + r, y + r);  // 用椭圆模拟正圆
    DeleteObject(brush);
}

// 更新并绘制所有粒子
void updateAndDrawParticles() {
    for (int i = 0; i < partCount; i++) {
        Particle* p = &particles[i];

        // 1. 更新粒子状态（加入轻微重力，更自然）
        p->x += p->vx;
        p->y += p->vy;
        p->vy += 0.03;  // 重力加速度（缓慢下落）
        p->life--;      // 减少生命周期

        // 2. 绘制粒子
        SetDCBrushColor(hdc, p->col);
        drawSolidCircle((int)p->x, (int)p->y, p->r);

        // 3. 移除死亡粒子（用最后一个粒子覆盖，优化性能）
        if (p->life <= 0) {
            particles[i] = particles[--partCount];
            i--;  // 重新检查当前索引（避免漏判）
        }
    }
}

// 窗口消息处理函数（核心逻辑）
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        hdc = BeginPaint(hwnd, &ps);

        // 1. 黑色背景（清空屏幕）
        SetDCBrushColor(hdc, RGB(0, 0, 0));
        Rectangle(hdc, 0, 0, WINDOW_W, WINDOW_H);

        // 2. 每帧生成10个新粒子（维持实心填充效果）
        for (int i = 0; i < 10; i++) {
            initParticle();
        }

        // 3. 更新并绘制所有粒子
        updateAndDrawParticles();

        EndPaint(hwnd, &ps);
        Sleep(12);  // 控制帧率（约83帧/秒，流畅无卡顿）
        InvalidateRect(hwnd, NULL, FALSE);  // 触发下一次重绘（循环）
        return 0;
    }

    case WM_DESTROY:
        PostQuitMessage(0);  // 关闭窗口时退出程序
        return 0;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

// 主函数（注册窗口+启动消息循环）
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) {
    srand((unsigned int)time(NULL));  // 初始化随机数种子（粒子属性随机）

    // 1. 注册窗口类
    const char CLASS_NAME[] = "SolidHeartParticle";
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WindowProc;    // 绑定消息处理函数
    wc.hInstance = hInst;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    RegisterClass(&wc);

    // 2. 创建窗口
    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "动态粒子实心爱心",  // 窗口标题
        WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX,  // 禁止最大化（保持窗口比例）
        CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_W, WINDOW_H,
        NULL, NULL, hInst, NULL
    );
    if (hwnd == NULL) return 0;

    // 3. 显示窗口并启动消息循环（程序主循环）
    ShowWindow(hwnd, nCmdShow);
    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
